/*
 * 描述： <描述>
 * 修改人： rain
 * 修改时间： 2016年10月10日
 * 项目： rainhy-webcore
 */
package com.rainhy.property.manager.springdata;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import javax.annotation.Resource;

import org.apache.commons.lang3.ClassUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rainhy.core.exception.RainhyNoSuchSwitchException;
import com.rainhy.core.util.JsonUtils;
import com.rainhy.property.context.PropertyContext;
import com.rainhy.property.context.PropertyManager;
import com.rainhy.property.manager.springdata.bean.PropertyValue;
import com.rainhy.property.manager.springdata.entity.PropertyDataType;
import com.rainhy.property.manager.springdata.entity.PropertyEntity;
import com.rainhy.property.manager.springdata.service.PropertyService;

/**
 * springdata 属性管理器<br>
 * XXX 里面的属性类类型转换先固定.以后有时间,再进行重构
 * 
 * @author rain
 * @version [版本号, 2016年10月10日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class SpringDataPropertyManager implements PropertyManager {
    
    /** 日志 */
    private final Logger logger = LoggerFactory.getLogger(PropertyContext.class);
    
    @Resource(name = "com.rainhy.property.manager.springdata.service")
    private PropertyService service;
    
    /** 类型转换器 */
    private Map<Class<?>, PropertyDataType> propertyDataTypeMapping = new WeakHashMap<>();
    
    /** 统计属性条数 */
    public long count() {
        return service.count();
    }
    
    @Override
    public void deleteProperties(String propertyKey) {
        this.service.deleteByKey(propertyKey);
    }
    
    @Override
    public void deleteProperty(String propertyType, String propertyKey) {
        this.service.deleteByTypeAndKey(propertyType, propertyKey);
    }
    
    @Override
    public Object getProperty(String propertyType, String propertyKey) {
        PropertyEntity propertyEntity = service.findByTypeAndKey(propertyType, propertyKey);
        if (propertyEntity == null) {
            return null;
        }
        return getProperty(propertyEntity);
    }
    
    @Override
    public String propertyType() {
        return null;    // null能管理所有类型的属性
    }
    
    @Override
    public void removeProperty(String propertyType, String propertyKey) {
        PropertyEntity property = this.service.findByTypeAndKey(propertyType, propertyKey);
        if (property != null) {
            this.service.deleteById(property.getId());
        }
    }
    
    @Override
    public void setProperty(String propertyType, String propertyKey, Object property) {
        PropertyEntity propertyEntity = service.findByTypeAndKey(propertyType, propertyKey);
        if (propertyEntity == null) {
            propertyEntity = new PropertyEntity();
            propertyEntity.setPropertyKey(propertyKey);
            propertyEntity.setPropertyType(propertyType);
            propertyEntity.setPropertyDataType(getPropertyDataType(property));
        }
        
        switch (propertyEntity.getPropertyDataType()) {
            case Null:
                propertyEntity.setPropertyOfString(null);
                propertyEntity.setPropertyOfBigDecimal(null);
                propertyEntity.setPropertyOfBoolean(null);
                propertyEntity.setPropertyOfInteger(null);
                propertyEntity.setPropertyOfDate(null);
                break;
            case String:
                propertyEntity.setPropertyOfString((String) property);
                break;
            case BigDecimal:
                propertyEntity.setPropertyOfBigDecimal((BigDecimal) property);
                break;
            case Long:
                propertyEntity.setPropertyOfBigDecimal(BigDecimal.valueOf((Long) property));
                break;
            case Boolean:
                propertyEntity.setPropertyOfBoolean((Boolean) property);
                break;
            case Integer:
                propertyEntity.setPropertyOfInteger((Integer) property);
                break;
            case Date:
                propertyEntity.setPropertyOfDate((Date) property);
                break;
            case DateTime:
                propertyEntity.setPropertyOfDate(((DateTime) property).toDate());
                break;
            case Map:
                propertyEntity.setPropertyOfString(JsonUtils.toJson(new PropertyValue((Map<?, ?>) property)));
                break;
            case List:
                propertyEntity.setPropertyOfString(JsonUtils.toJson(new PropertyValue((List<?>) property)));
                break;
            default:
                throw new RainhyNoSuchSwitchException("未知的属性类类型: {}", String.valueOf(propertyEntity.getPropertyDataType()));
        }
        service.save(propertyEntity);
    }
    
    /** 获取属性 */
    private Object getProperty(PropertyEntity propertyEntity) {
        String string = propertyEntity.getPropertyOfString();
        BigDecimal bigDecimal = propertyEntity.getPropertyOfBigDecimal();
        switch (propertyEntity.getPropertyDataType()) {
            case Null:
                return null;
            case BigDecimal:
                return bigDecimal;
            case Long:
                return bigDecimal == null ? null : Long.valueOf(bigDecimal.longValue());
            case Boolean:
                return propertyEntity.getPropertyOfBoolean();
            case Date:
                return propertyEntity.getPropertyOfDate();
            case DateTime:
                return propertyEntity.getPropertyOfDate() == null ? null : new DateTime(propertyEntity.getPropertyOfDate());
            case Integer:
                return propertyEntity.getPropertyOfInteger();
            case String:
                return string;
            case Map:
                return JsonUtils.fromJson(string, PropertyValue.class).getMap();
            case List:
                return JsonUtils.fromJson(string, PropertyValue.class).getList();
            default:
                throw new RainhyNoSuchSwitchException("未知的属性类类型: {}" + String.valueOf(propertyEntity.getPropertyDataType()));
        }
    }
    
    /** 根据类型获取类型编号 */
    private PropertyDataType getPropertyDataType(Object property) {
        if (property == null) {
            return PropertyDataType.Null;
        }
        
        Class<?> propertyClass = property.getClass();
        PropertyDataType propertyDataType = propertyDataTypeMapping.get(propertyClass);
        if (propertyDataType != null) {
            return propertyDataType;
        }
        propertyDataType = PropertyDataType.Unknown;
        if (ClassUtils.isAssignable(propertyClass, String.class)) {
            propertyDataType = PropertyDataType.String;
        } else if (ClassUtils.isAssignable(propertyClass, BigDecimal.class)) {
            propertyDataType = PropertyDataType.BigDecimal;
        } else if (ClassUtils.isAssignable(propertyClass, Boolean.class)) {
            propertyDataType = PropertyDataType.Boolean;
        } else if (ClassUtils.isAssignable(propertyClass, Integer.class)) {
            propertyDataType = PropertyDataType.Integer;
        } else if (ClassUtils.isAssignable(propertyClass, Date.class)) {
            propertyDataType = PropertyDataType.Date;
        } else if (ClassUtils.isAssignable(propertyClass, DateTime.class)) {
            propertyDataType = PropertyDataType.DateTime;
        } else if (ClassUtils.isAssignable(propertyClass, Long.class)) {
            propertyDataType = PropertyDataType.Long;
        } else if (ClassUtils.isAssignable(propertyClass, List.class)) {
            propertyDataType = PropertyDataType.List;
        } else if (ClassUtils.isAssignable(propertyClass, Map.class)) {
            propertyDataType = PropertyDataType.Map;
        }
        
        if (PropertyDataType.Unknown == propertyDataType) {
            logger.warn("未知的类型属性: {}", propertyClass.getName());
        }
        
        propertyDataTypeMapping.put(propertyClass, propertyDataType);
        return propertyDataType;
    }
    
}
